在實作-會員系統(一)-會員註冊(一)中,我們已經讓專案跟資料庫做聯動。在該篇分享將接續把資料寫入資料庫中,完成「會員註冊」這部分的功能。
.
├── app.js
├── bin
│ └── www
├── config
│ └── development_config.js
├── controllers
│ └── modify_controller.js
├── models
│ ├── connection_db.js
│ └── register_model.js
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── member.js
│ └── users.js
├── sevice
│ └── member_check.js
├── views
├── error.ejs
└── index.ejs
├── .env
└── .gitignore
接下來,我們先將集中在routes
資料夾的member.js
檔案,其程式碼拆分成MVC
的架構。並賦予這個API的url為/register
。
var express = require('express');
var router = express.Router();
const MemberModifyMethod = require('../controllers/modify_controller');
memberModifyMethod = new MemberModifyMethod();
router.post('/register', memberModifyMethod.postRegister);
module.exports = router;
並使其對應到controllers
資料夾的modify_controller.js
檔案。
const toRegister = require('../models/register_model');
module.exports = class Member {
postRegister(req, res, next) {
// 獲取client端資料
const memberData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
create_date: onTime()
}
// 將資料寫入資料庫
toRegister(memberData).then(result => {
// 若寫入成功則回傳
res.json({
status: "註冊成功。",
result: result
})
}, (err) => {
// 若寫入失敗則回傳
res.json({
result: err
})
})
}
}
//取得現在時間,並將格式轉成YYYY-MM-DD HH:MM:SS
const onTime = () => {
const date = new Date();
const mm = date.getMonth() + 1;
const dd = date.getDate();
const hh = date.getHours();
const mi = date.getMinutes();
const ss = date.getSeconds();
return [date.getFullYear(), "-" +
(mm > 9 ? '' : '0') + mm, "-" +
(dd > 9 ? '' : '0') + dd, " " +
(hh > 9 ? '' : '0') + hh, ":" +
(mi > 9 ? '' : '0') + mi, ":" +
(ss > 9 ? '' : '0') + ss
].join('');
}
為了讓client端的資料傳入資料庫,我們在models
資料夾的register_model.js
檔案中寫入:
const db = require('./connection_db');
module.exports = function register(memberData) {
let result = {};
return new Promise((resolve, reject) => {
// 將資料寫入資料庫
db.query('INSERT INTO member_info SET ?', memberData, function (err, rows) {
// 若資料庫部分出現問題,則回傳給client端「伺服器錯誤,請稍後再試!」的結果。
if (err) {
console.log(err);
result.status = "註冊失敗。"
result.err = "伺服器錯誤,請稍後在試!"
reject(result);
return;
}
result.registerMember = memberData;
resolve(result);
})
})
}
之後,我們使用Postman來測試看看:
接著進入到MySQL資料庫,使用select * from member_info;
指令來顯示看看是否有將資料寫入資料庫中。
我們在「寫入資料庫」的步驟中,只將資料輸入後就結束,還未達成「不允許相同帳號(email)重複註冊」的需求。若要達成這個需求,代表我們要把資料寫入資料庫前,應該要先確定資料庫是否有該筆資料。等同於我們需要先將資料庫的資料撈出來比對一次,如果真的沒有該筆資料才進行寫入動作。否則,就會跳出已有重複的Email。
的回應。
在這部分我們只要將models
資料夾中的register_model
檔案,改寫成:
const db = require('./connection_db');
module.exports = function register(memberData) {
let result = {};
return new Promise((resolve, reject) => {
// 尋找是否有重複的email
db.query('SELECT email FROM member_info WHERE email = ?', memberData.email, function (err, rows) {
// 若資料庫部分出現問題,則回傳給client端「伺服器錯誤,請稍後再試!」的結果。
if (err) {
console.log(err);
result.status = "註冊失敗。"
result.err = "伺服器錯誤,請稍後在試!"
reject(result);
return;
}
// 如果有重複的email
if (rows.length >= 1) {
result.status = "註冊失敗。";
result.err = "已有重複的Email。";
reject(result);
} else {
// 將資料寫入資料庫
db.query('INSERT INTO member_info SET ?', memberData, function (err, rows) {
// 若資料庫部分出現問題,則回傳給client端「伺服器錯誤,請稍後再試!」的結果。
if (err) {
console.log(err);
result.status = "註冊失敗。";
result.err = "伺服器錯誤,請稍後在試!"
reject(result);
return;
}
// 若寫入資料庫成功,則回傳給clinet端下:
result.status = "註冊成功。"
result.registerMember = memberData;
resolve(result);
})
}
})
})
}
完成後,我們再透過Postman來做測試。接著email欄位的值也保持為test@gmail.com
,並按send按鈕送出。在response的回傳就能會看到:
{
"result": {
"status": "註冊失敗。",
"err": "已有重複的Email。"
}
}
這邊我們在service
資料夾的member_check.js
檔案,先來撰寫判斷email的規則。這邊我們將使用正規表達示來解決:
module.exports = class CheckCustomer {
//判斷email格式
checkEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const result = re.test(email);
return result;
}
}
並在controllers
資料夾的modify_controller.js
中,加入這層的判斷:
const toRegister = require('../models/register_model');
const Check = require('../service/member_check');
check = new Check();
module.exports = class Member {
postRegister(req, res, next) {
// 獲取client端資料
const memberData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
create_date: onTime()
}
const checkEmail = check.checkEmail(memberData.email);
// 不符合email格式
if (checkEmail === false) {
res.json({
result: {
status: "註冊失敗。",
err: "請輸入正確的Eamil格式。(如1234@email.com)"
}
})
// 若符合email格式
} else if (checkEmail === true) {
// 將資料寫入資料庫
toRegister(memberData).then(result => {
// 若寫入成功則回傳
res.json({
result: result
})
}, (err) => {
// 若寫入失敗則回傳
res.json({
err: err
})
})
}
}
}
接著,我們一樣使用Postman來測試,並將email欄位的值改成任何非正常mail格式的值。並按send按鈕送出。在response的回傳就能會看到:
{
"result": {
"status": "註冊失敗。",
"err": "請輸入正確的Eamil格式。(如1234@email.com)"
}
}
這邊我們會需要用到Node.js的內置模組crypto中的hash部分。
讀者可能會問「為什麼我們需要做到密碼加密呢?」。試想著,如果我們寫入資料庫的密碼沒有進行加密
,等同於是明碼
的狀態。假設公司負責資料庫的工程師心懷不軌,或是資料庫遭受攻擊,導致整個資料外洩,那不就有可能造成很嚴重的損失。所以,我們才需要將密碼做個加密的動作,且不可逆。至於,加密的方式有很多,而這邊先展示最簡單(安全性最低)的加密方式。
註記:不可逆表示密碼不會隨著我們加密的方式而反轉回來。
首先,我們到models
資料夾的encryption.js
檔案中寫入:
const crypto = require('crypto');
module.exports = function getRePassword(password) {
//加密
let hashPassword = crypto.createHash('sha1');
hashPassword.update(password);
const rePassword = hashPassword.digest('hex');
// console.log('rePassword: ' + rePassword);
return rePassword;
}
並回到controllers
資料夾的modify_controller.js
檔案中加入加密部分的判斷:
const toRegister = require('../models/register_model');
const Check = require('../service/member_check');
const encryption = require('../models/encryption');
check = new Check();
module.exports = class Member {
postRegister(req, res, next) {
// 進行加密
const password = encryption(req.body.password);
// 獲取client端資料
const memberData = {
name: req.body.name,
email: req.body.email,
password: password,
create_date: onTime()
}
const checkEmail = check.checkEmail(memberData.email);
// 不符合email格式
if (checkEmail === false) {
res.json({
status: "註冊失敗。",
err: "請輸入正確的Eamil格式。(如1234@email.com)"
})
// 若符合email格式
} else if (checkEmail === true) {
// 將資料寫入資料庫
toRegister(memberData).then(result => {
// 若寫入成功則回傳
res.json({
result: result
})
}, (err) => {
// 若寫入失敗則回傳
res.json({
err: err
})
})
}
}
}
//取得現在時間,並將格式轉成YYYY-MM-DD HH:MM:SS
const onTime = () => {
const date = new Date();
const mm = date.getMonth() + 1;
const dd = date.getDate();
const hh = date.getHours();
const mi = date.getMinutes();
const ss = date.getSeconds();
return [date.getFullYear(), "-" +
(mm > 9 ? '' : '0') + mm, "-" +
(dd > 9 ? '' : '0') + dd, " " +
(hh > 9 ? '' : '0') + hh, ":" +
(mi > 9 ? '' : '0') + mi, ":" +
(ss > 9 ? '' : '0') + ss
].join('');
}
這時,我們一樣再透過Postman來進行測試。並將email欄位的值改成另外個不會重複的email。按send按鈕送出後,在response的回傳就能會看到我們的password已經被加密了。
{
"result": {
"status": "註冊成功。",
"registerMember": {
"name": "test",
"email": "test123@gmail.com",
"password": "7110eda4d09e062aa5e4a390b0a572ac0d2c0220",
"create_date": "2018-01-02 23:23:27"
}
}
}
這在我們已經完成了「會員註冊」的功能了,接下來會往「會員登入」部分前進。
不好意思,想請問中間一段
寫入資料庫
為了讓client端的資料傳入資料庫,我們在models資料夾的member_model.js檔案中寫入
這裡的資料夾是否改為 register_model.js ?
對,沒錯。這部分要改為 register_model.js
才對。
感謝 fustarky 幫忙校正。